#!/usr/bin/env python
import psgolftool, tohex

def test(input, expectedBinary):
  binaryData = psgolftool.convert_binary(input)
  if binaryData != expectedBinary:
    message = """Input: %s
Expected: %s
Received: %s""" % (tohex.tohex(input),
                   tohex.tohex(expectedBinary),
                   tohex.tohex(binaryData))
    raise message

def simpleTests():
  test('', '')
  test('0', '0')
  test('100', '\x88\x64')
  test('-10', '\x88\xf6')
  test('\x88\x00', '0')
  test('\x88\x64', '\x88\x64')
  test('\x88\xf6', '\x88\xf6')
  test('\x84\xff\xff\xff\x7f', '\x87\x7f\xff')
  test('\x84\x7f\xff\xff\xff', '\x85\xff\xff\xff\x7f')
  test('\x85\xff\xff\xff\x7f', '\x85\xff\xff\xff\x7f')
  test('\x85\x7f\xff\xff\xff', '\x87\x7f\xff')
  test('1 2', '1 2')
  test('1\t2', '1 2')
  test('1\n2', '1 2')
  test('1 2 3', '1\x88\x023')
  test('10 1', '\x88\n1') # Alternative: \x88\x0a1
  test('10000', '\x87\x10\'')
  test('2147483647', '\x85\xff\xff\xff\x7f')
  test('_', '_')
  test('=only', '=only')
  test('if', 'if')
  test('if 0', '\x92T0')
  test('aload', '\x92\x02')
  test('counttomark', '\x92\x1b')
  test('readhexstring', '\x92|')
  test('/?', '/?')
  test('/@', '/@')
  test('/A', '/A')
  test('/s()def', '/s()\x923')
  test('/. 0 def . =', '/. 0\x923. =')
  test('/? 0 def ? =', '/? 0\x923? =')
  test('/_ 0 def _ =', '/_ 0\x923_ =')
  test('/# 0 def # =', '/# 0\x923# =')
  test('. 1', '. 1')
  test('.1', '.1')
  test('-.1', '-.1')
  test('1.0', '1.0')
  test('-1.0', '-1.0')
  test('\x88\x64', '\x88\x64')
  test('\x92|', '\x92|')
  test('\x92\x3d', 'eq')
  test('/ =', '/ =')
  test('1 2\x92\x01', '1 2\x92\x01')
  test('<<>>', '<<>>')
  test('<<1 2>>', '<<1 2>>')
  test('[1 2>>', '[1 2>>')
  test('1\r\n2', '1 2') # Windows line endings.
  test('[', '[')
  test(']', ']')
  test('[]', '[]')
  test('{}', '{}')
  test('/ /', '/ /')
  test('/_ /', '/_/')
  test('/!/ /!', '/!/ /!')
  test('[>>\x92\x0d', '[>>\x92\x0d')
  test('[/a count>>a =', '[/a\x92\x1a>>a =')

def string_tests():
  test('()', '()')
  test('(a)', '(a)')
  test('(\))', '\x8e\x01)')
  test(r'(\\)', '\x8e\x01\\')
  test(r'(\0)', '<0>')
  test('\x8e\x01\x08', '(\x08)')
  test(r'(\b)', '(\x08)')
  test('(\t)', '(\t)')
  test(r'(\t)', '(\t)')
  test(r'(\13)', '(\x0b)')
  test(r'(\177)', '(\x7f)')
  test(r'(\0000)', '(\x000)')
  test(r'(\08)', '(\x008)')
  test('\x8e\x01a', '(a)')
  test('\x8e\x01)', '\x8e\x01)')
  test('(a)(a)eq', '(a)(a)eq')
  test('(())','(())')
  test('((\)))','\x8e\x03())')
  test('<30>', '(0)')
  test('<9>', '<9>')
  test('(\x90)', '<9>')
  test('(\r)', '(\n)') # \r is converted to \n !
  test(r'(\r)', '\x8e\x01\r')
  test('(\n)', '(\n)')
  test('\x8f\x00\x01=', '(=)')
  #test('\x8f\x01\x00=', None)
  #test('\x90\x00\x01=', None)
  test('\x90\x01\x00=', '(=)')
  test('(' + '\\r'*256 + ')', '\x8f\x01\x00'+'\r'*256)

def homogeneous_array_tests():
  test('\x95\x00\x00\x01\x12\x34\x56\x78', '{\x85\x78\x56\x34\x12}') # 32-bit, high byte first.
  test('\x95\x20\x00\x02\x12\x34\x56\x78', '\x95\x20\x00\x02\x12\x34\x56\x78') # 16-bit, high byte first.
  test('\x95\x80\x01\x00\x12\x34\x56\x78', '{\x85\x12\x34\x56\x78}') # 32-bit, low byte first.
  test('\x95\xa0\x02\x00\x12\x34\x56\x78', '\x95\x20\x00\x02\x34\x12\x78\x56') # 16-bit, low byte first.
  test('\x95\x00\x00\x01\xff\xff\xff\xff', '{-1}')
  test('\x95\x20\x00\x01\xff\xff', '{-1}')
  test('{315 370 -277}', '\x95\x20\x00\x03\x01\x3b\x01\x72\xfe\xeb')
  test('{2 3 4}', '{2\x88\x034}')
  
def minimiseBinaryTests():
  test('10', '\x88\n') # Alternative: 10
  test('11', '11')
  test('1 1', '1 1')
  test('11 11', '\x88\x0b11')
  test('{11}', '{11}')
  test('11 neg', '11\x92n')
  test('127 neg', '\x88\x7f\x92n')
  test('128 neg', '128\x92n')
  test('1 = 2 =', '1 =\x88\x02=')
  test('1 2 add', '1 2\x92\x01')
  test('1 2 gt =', '1 2\x92P=')
  test('/x 5 def x =', '/x 5\x923x =')

def complexTests():
  plaintext = '127{/s(%stdin)(r)file 30 string readline{def s(?)search 2 index 0 48 1 57{put s length 15 gt{1 1 s{2 index mul add exch 2 xor exch}forall 10}{14 0 s{30 mod 2 index mul add exch 1 sub exch}forall 11}ifelse mod 3 eq{s =}if pop 2 index 0}for}if}repeat'
  binarydata = '\x88\x7f{/s(%stdin)(r)\x92A30\x92\xa5\x92}{\x923s(?)\x92\x8d2\x92X0\x8801\x889{\x92xs\x92b15\x92P{1\x88\x01s{2\x92X\x92l\x92\x01\x92>2\x92\xc1\x92>}\x92I\x88\n}{\x88\x0e0 s{30\x92j2\x92X\x92l\x92\x01\x92>1\x92\xa9\x92>}\x92I11}\x92U\x92j3\x92={s =}if\x92u2\x92X0}\x92H}if}\x92\x83'
  test(plaintext, binarydata)

  plaintext = '0 -2 1/s(%stdin)(r)file 1001 string readline pop def s length 1 sub{s exch 3{getinterval{1 and add 2 mul}forall neg 8 exit ne{stop}if( )}stopped{(#)}if print 0}for'
  binarydata = '0\x88\xfe1/s(%stdin)(r)\x92A\x87\xe9\x03\x92\xa5\x92}\x92u\x923s\x92b1\x92\xa9{s\x92>3{\x92L{1\x92\x04\x92\x012\x92l}\x92I\x92n8\x92\x40ne{\x92\xa2}if( )}\x92\xa3{(#)}if\x92v0}\x92H'
  test(plaintext, binarydata)

def main():
  simpleTests()
  string_tests()
  homogeneous_array_tests()
  minimiseBinaryTests()
  complexTests()

if __name__ == '__main__':
  main()
